/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc.  All rights reserved
// Delay queue
// C++ header

#ifndef _DELAY_QUEUE_HH
#define _DELAY_QUEUE_HH

#ifndef _NET_COMMON_H

#include "../../groupsock/include/NetCommon.h"

#endif

#ifdef TIME_BASE
typedef TIME_BASE time_base_seconds;
#else
typedef long time_base_seconds;
#endif

///// A "Timeval" can be either an absolute time, or a time interval /////

class Timeval {
public:
    time_base_seconds seconds() const {
        return fTv.tv_sec;
    }

    time_base_seconds seconds() {
        return fTv.tv_sec;
    }

    time_base_seconds useconds() const {
        return fTv.tv_usec;
    }

    time_base_seconds useconds() {
        return fTv.tv_usec;
    }

    int operator>=(Timeval const &arg2) const;

    int operator<=(Timeval const &arg2) const {
        return arg2 >= *this;
    }

    int operator<(Timeval const &arg2) const {
        return !(*this >= arg2);
    }

    int operator>(Timeval const &arg2) const {
        return arg2 < *this;
    }

    int operator==(Timeval const &arg2) const {
        return *this >= arg2 && arg2 >= *this;
    }

    int operator!=(Timeval const &arg2) const {
        return !(*this == arg2);
    }

    void operator+=(class DelayInterval const &arg2);

    void operator-=(class DelayInterval const &arg2);
    // returns ZERO iff arg2 >= arg1

protected:
    Timeval(time_base_seconds seconds, time_base_seconds useconds) {
        fTv.tv_sec = seconds;
        fTv.tv_usec = useconds;
    }

private:
    time_base_seconds &secs() {
        return (time_base_seconds &) fTv.tv_sec;
    }

    time_base_seconds &usecs() {
        return (time_base_seconds &) fTv.tv_usec;
    }

    struct timeval fTv;
};

#ifndef max

inline Timeval max(Timeval const &arg1, Timeval const &arg2) {
    return arg1 >= arg2 ? arg1 : arg2;
}

#endif
#ifndef min

inline Timeval min(Timeval const &arg1, Timeval const &arg2) {
    return arg1 <= arg2 ? arg1 : arg2;
}

#endif

class DelayInterval operator-(Timeval const &arg1, Timeval const &arg2);
// returns ZERO iff arg2 >= arg1


///// DelayInterval /////

class DelayInterval : public Timeval {
public:
    DelayInterval(time_base_seconds seconds, time_base_seconds useconds)
            : Timeval(seconds, useconds) {}
};

DelayInterval operator*(short arg1, DelayInterval const &arg2);

extern DelayInterval const DELAY_ZERO;
extern DelayInterval const DELAY_SECOND;
extern DelayInterval const DELAY_MINUTE;
extern DelayInterval const DELAY_HOUR;
extern DelayInterval const DELAY_DAY;

///// _EventTime /////

class _EventTime : public Timeval {
public:
    _EventTime(unsigned secondsSinceEpoch = 0,
               unsigned usecondsSinceEpoch = 0)
    // We use the Unix standard epoch: January 1, 1970
            : Timeval(secondsSinceEpoch, usecondsSinceEpoch) {}
};

_EventTime TimeNow();

extern _EventTime const THE_END_OF_TIME;


///// DelayQueueEntry /////

class DelayQueueEntry {
public:
    virtual ~DelayQueueEntry();

    intptr_t token() {
        return fToken;
    }

protected: // abstract base class
    DelayQueueEntry(DelayInterval delay);

    virtual void handleTimeout();

private:
    friend class DelayQueue;

    DelayQueueEntry *fNext;
    DelayQueueEntry *fPrev;
    DelayInterval fDeltaTimeRemaining;

    intptr_t fToken;
    static intptr_t tokenCounter;
};

///// DelayQueue /////

class DelayQueue : public DelayQueueEntry {
public:
    DelayQueue();

    virtual ~DelayQueue();

    void addEntry(DelayQueueEntry *newEntry); // returns a token for the entry
    void updateEntry(DelayQueueEntry *entry, DelayInterval newDelay);

    void updateEntry(intptr_t tokenToFind, DelayInterval newDelay);

    void removeEntry(DelayQueueEntry *entry); // but doesn't delete it
    DelayQueueEntry *removeEntry(intptr_t tokenToFind); // but doesn't delete it

    DelayInterval const &timeToNextAlarm();

    void handleAlarm();

private:
    DelayQueueEntry *head() { return fNext; }

    DelayQueueEntry *findEntryByToken(intptr_t token);

    void synchronize(); // bring the 'time remaining' fields up-to-date

    _EventTime fLastSyncTime;
};

#endif
